/*
 * conexionPPD.c
 *
 *  Created on: 05/10/2011
 *      Author: diegoar27
 */
#include "../PDD.h"

extern t_array arrayConexiones;

/*Handshake con el dispositivo*/
char *crearHandshake(){
	t_config *config = getConfig();
	char *handshake = (char*)malloc(sizeof(DEFAULT_BUFFER_SIZE));
	int tmp_size = 0;

	/*Tipo\length\idDisco\ip\cilindros\cabezas\sectores*/
	memcpy(handshake,0,tmp_size = sizeof(char));
	memcpy(handshake + tmp_size, 0 , tmp_size += sizeof(short)); /* PREGUNTAR SI EL LENGTH DEBE SER 0 o DEBE SER EL TOTAL DEL MENSAJE */
	memcpy(handshake + tmp_size, config->idDisco , tmp_size += sizeof(int));
	tmp_size += sizeof(config->myIp);
	memcpy(handshake + tmp_size, config->myIp , tmp_size += sizeof(char));
	memcpy(handshake + tmp_size, config->cilindros , tmp_size += sizeof(int));
	memcpy(handshake + tmp_size, config->cabezas , tmp_size += sizeof(int));
	memcpy(handshake + tmp_size, config->sectores , tmp_size += sizeof(int));

	return handshake;
}

char *crearRespuestaHandshake(uint32_t error){
	int tmp_size = 0;
	t_config *config = getConfig();
	char *request = (char*)malloc(sizeof(DEFAULT_BUFFER_SIZE));
	/* TODO TERMINAR CREAR RESPUESTA */

	memcpy(request,0,tmp_size = sizeof(char));
	memcpy(request + tmp_size, 0 , tmp_size += sizeof(short)); /* PREGUNTAR SI EL LENGTH DEBE SER 0 o DEBE SER EL TOTAL DEL MENSAJE */
	memcpy(request + tmp_size, config->idDisco , tmp_size += sizeof(int));
	tmp_size += sizeof(config->myIp);
	memcpy(request + tmp_size, config->myIp , tmp_size += sizeof(char));

	/*Si payload vacio! => no hay error*/
	if (error > 0){
		char *errorDesc = getDescripcionDeError(error);
		tmp_size += sizeof(errorDesc);
		memcpy(request + tmp_size, errorDesc, tmp_size += sizeof(char));
	}

	return request;
}

int analizarHandshake(char *mensaje){
	char *ptr;
	int tmp_size=0;
	u_int32_t error = 0;
	t_config *config = getConfig();

	memcpy (ptr,mensaje, tmp_size += sizeof(char) );
	/*Es handshake*/
	/*Si me llega un handshake unicamente puede ser del PFS*/
	if (strcmp(ptr,"0") == 0){
		/*Deberia ser vacío*/
		memcpy (ptr,mensaje, tmp_size += (sizeof(short) + sizeof(int)) );/* Suma el length y el idDisco del mensaje */
		/*Deberia traer la ip del PFS*/
		memcpy (ptr,mensaje, tmp_size += 15 * sizeof(char)); /* Leo 15 caracteres porque porque es como si tuviera la mascara completa xxx.xxx.xxx.xxx */

		logMessage(LOG_INFO,"ip del PFS %s",ptr);
		//Logeo la ip del PFS
		//memcpy(config->ip_Dispositivo,ptr, strlen(ptr));
		error=0;
	}
	else{
		logMessage(LOG_ERR,"no se pudo cargar la configuracion.");
		error = 1;
	}

	return error;

}

int analizarRespuestaHandshake(char *respuesta){
	char *ptr;
	int tmp_size = 0;
	u_int32_t error = 0;

	memcpy (ptr,respuesta, tmp_size += sizeof(char));

	/*Es handshake*/
	/*Si me llega un handshake unicamente puede ser del PFS*/
	if (strcmp(ptr,"0") == 0){
		memcpy (ptr,respuesta, tmp_size += sizeof(short));
		memcpy (ptr,respuesta, tmp_size += (sizeof(char) * 2) ); /* Debe leer unicamente el STRING "OK" por eso es char*2, si trae otra cosa se produjo error en handshake. */
		if (strcmp(ptr,"OK")){
			return 1;
		}
	}
	return 0;
}
/**/

void analizarMensaje(char *mensaje){

	char *ptr = (char*)malloc(sizeof(char));
	u_int32_t error = 0;
	t_config *config = getConfig();

	memcpy(ptr,mensaje,sizeof(char));

	if ((strcmp(ptr,"1") == 0) || (strcmp(ptr,"2") == 0)){
		t_list *list = getListPeticiones(config->algoritmoPlanificacion);
		t_peticion *peticion = peticionStringToPeticion(mensaje);
		agregarPeticionACola(peticion);
	}
	else /*Error*/
	{
		logMessage(LOG_ERR,"ERROR en analizar mensaje! tipo peticion:%s",ptr);
		error = 1;
	}

/*VER*/	if (error == 1){
		/*Envio error como respuesta*/}
	free(ptr);
}

int enviarRespuestaHandshake(t_socket_client *client,uint32_t error){
	return enviarRespuesta(client, crearRespuestaHandshake(error));
}
/**/

/*Thread*/
void *inicializarComunicacion(){
	t_config *config = getConfig();

	if (config->modoInicio == CONNECT){
		/*
		 * Me conecto al raid.. envio handshake si me acepta la comuniacion me quedo
		 * a la espera de peticiones.
		 */
		logMessage(LOG_INFO,"generarSocketRAID");
		generarSocketRAID();
	}else if (config->modoInicio == LISTEN)
	{
		logMessage(LOG_INFO,"generarSokcetPFS");
		generarSokcetPFS();
		/*
		 * Me quedo escuchadno a la espera que el PFS se conecte.. una vez que me envia
		 * el handshake si es OK! respondo e inicializo el poll de conexiones
		 */
	}

}

/*RAID*/
void generarSocketRAID(){
	t_config *config = getConfig();
	t_socket_client *client = sockets_createClient(config->myIp, config->puertoRecv);
	logMessage(LOG_INFO,"generarSokcetPFS .. hago conect %s %d",config->myIp, strlen(config->myIp));
	if (client != NULL){
		logMessage(LOG_INFO,"generarSokcetPFS .. hago conect");
		if (sockets_connect(client, config->ipDispo,config->puertoDispRecv) > 0){

			if (enviarRespuesta(client, crearHandshake()) > 0){

				t_socket_buffer *buffer = sockets_recv(client);

				if (buffer != NULL && analizarRespuestaHandshake(buffer->data) > 0){
					generarSocketEscuchaRAID(client);
				}
				else
					logMessage(LOG_WARN,"Mal el handshake *generarSocketRAID*");
			}
		}
		else{
			logMessage(LOG_ERR,"No se pudo generar el Socket RAID");
		}
	}
	else
		logMessage(LOG_ERR,"WTF???CLIENTE NULL");
}

/*RAID*/
void generarSocketEscuchaRAID(t_socket_client *client){
	t_config *config = getConfig();
	while (1){

		t_socket_buffer *buffer = sockets_recv(client);
		if (buffer != NULL){
			analizarMensaje(buffer->data);
		}
		else{
			logMessage(LOG_ERR,"Buffer nulo.");
		}
	}
}

/*
 * Thread q establece la conexion con el RAID o FileSys
 */
void *generarSocketEnvioRAID(){
	t_config *config = getConfig();

	int count = 0;
	t_socket_client *myClient = sockets_createClient(config->myIp, config->puertoSend);
	t_socket_client *clientSend = sockets_connect(myClient, config->ipDispo, config->puertoDispSend);
	if (clientSend != NULL){

		while (1){
			t_list *listaResuelto = getListPeticionesResueltas();
			while (collection_list_size(listaResuelto) > 0){
				t_peticion *p = collection_list_remove(listaResuelto,0);
				if (p !=NULL){
					if (enviarRespuesta(clientSend, peticionToPeticionString(p->info)) > 0){
						logMessage(LOG_INFO,"Respuesta generarSocketEnvioRAID T.Oper:%s Sector:%d",p->tOperacion,p->nSector);
						destroyPeticion(p);
					}
					else{
						logMessage(LOG_ERR,"Error al enviar una respuesta");
					}
				}
			}
		}
	}
}

/*PFS*/
void generarSokcetPFS(){

	int error = 0;
	t_config *config = getConfig();
	t_socket_server*server = sockets_createServer(config->myIp,config->puertoRecv);

	t_socket_client *client = sockets_accept(server);
	if (client != NULL){
		t_socket_buffer *buffer = sockets_recv(client);
		if (buffer != NULL){
			//Analizo handshake
			error = analizarHandshake(buffer->data);
			/*Si esta t0d0 OK envio respuesta al handshake ok!*/
			if (enviarRespuestaHandshake(client,error) > 0){
				/*Si el envio de respuesta fue Ok! genero Poll*/
				if (error == 0){
					inicializarPollConexPFS();
				}
			}
			else{
				logMessage(LOG_ERR,"No se pudo enviar respuesta Handshake");
			}
		}
		//libero buffer y socket dado que la comunicacion se va a establer por el poll
		sockets_bufferDestroy(buffer);
	}
	sockets_destroyClient(client);
	/*Que pasa si se cae la conexion con el PFS?
	 * se vuelve a establecer??
	 */
	sockets_destroyServer(server);
}

/*PFS*/

/*
 * Genero thread y hago el canal de conexion con el socket PFS y PPD
 * No deberia cortar nunca el canal de comunicacion
 */
void *iniciarSocketServerPFS(t_socket_server *server){
	t_socket_client*client = sockets_accept(server);
	if (client != NULL){

		while (1)
		{
			t_socket_buffer *buffer = sockets_recv(client);
			if (buffer != NULL){

				analizarMensaje(buffer->data);

				/*Tomo de una lista mi paquete a responder*/
				/*VER QUE ESTO ESTA MAL*/ /*QUE PASA SI TODAVIA NO ESTA LA PETICION RESUELTA*/
				t_peticionResuelta *peticionResuelta = getPeticionResueltaByClient(client);
				if (peticionResuelta != NULL){

					char *peticionToString = peticionToPeticionString(peticionResuelta->peticionResuelta);

					if (sockets_sendString(client, peticionToString) > 0){
						logMessage(LOG_INFO,"Se envio respuesta *iniciarSocketServerPFS* %s ",sockets_getIp(client->socket));
					}
					else{
						logMessage(LOG_ERR,"ERROR SOCKET SEND *iniciarSocketServerPFS*");
					}

					destroyPeticion(peticionResuelta->peticionResuelta);
					free(peticionResuelta);
					free(peticionToString);
				}
			}
			else{
				logMessage(LOG_ERR,"Buffer Vacio");
			}
		}
	}
}

int enviarRespuesta(t_socket_client *client, char*stringToSend){
	return sockets_sendString(client, stringToSend);
}

void *disposeConexion(t_socket_server *server){
	sockets_destroyServer(server);
}

void inicializarPollConexPFS(){
	t_config *config = getConfig();
	int i;
	t_socket_server* server;
	t_array *arrayConexiones = array_create(config->tamPoll);

	for (i =0; i < config->tamPoll;i++){
		server = sockets_createServer(config->myIp, config->puertoPFSPoll + i);
		array_set(arrayConexiones,i,(void*)server,(void*)disposeConexion);

		generarThreadSocketPoll(server);
	}
}

void generarThreadSocketPoll(t_socket_server *server){
	pthread_t tid;
	if ( pthread_create(&tid, NULL, (void*)iniciarSocketServerPFS, (void*)server) <= 0) {
		logMessage(LOG_ERR,"Error al generar thread generarThreadSocketPoll");
	}
}
